Helpers for tracking file operations and computing diffs for display.
Mirror of the SDK's deepagents.backends.protocol.FILE_NOT_FOUND sentinel.
Hardcoded here rather than imported because file_ops is on the TUI import
path and importing deepagents at module scope violates the
startup-performance rule (see AGENTS.md); this module is dependency-free.
test_file_ops.py::test_file_not_found_matches_sdk is the drift guard that
fails if the SDK renames the sentinel.
Stand-in line marking where a diff body was clipped for display.
Written by compute_unified_diff and rendered as a truncated row. It is also
the signal that any counts recomputed from the body would be short — see
DiffMessage._recount, which returns None rather than a known-low number.
Match it with is_truncation_marker rather than by hand: the renderer and the
recount must agree, or a body that renders "diff truncated" is also counted as
if it were complete.
What a completed file operation can honestly say about what it changed.
One closed set rather than a set of independent booleans: the four states are mutually exclusive, and every consumer needs to agree on which one holds. Split across flags, a consumer that forgets one silently reports a change as fully displayed — which is how a delete whose pre-image was lost came to render identically to deleting an empty file.
shown: diff and diff_stats describe the change. Also the state for a
genuinely unchanged file and for operations that never compute a diff.untrusted_before: the pre-operation content could not be read, so
before_content is a stand-in empty string. Any diff against it is fiction —
an unchanged file looks like a no-op, a changed one like a whole-file
insertion — so diff_stats is None and the body must not be rendered.unreadable_after: the operation succeeded but its result could not be read
back, so there is nothing to diff. after_read_error carries the reason.terminators_only: the bytes changed but diff is None, because the change
lives entirely in line terminators, which splitlines() discards.Count added and removed lines in unified-diff lines.
Return what a successful file operation could not show, if anything.
Every case here leaves the caveat as the user's only account of a change the transcript cannot render, so each has to name what is missing. Silence would read as a complete report.
Covers all of DiffOutcome rather than the subset that mounts a diff: a
delete whose pre-image was lost produces no diff at all, so a caveat routed
only through the diff would leave destroying a 5,000-line file rendering
exactly like destroying an empty one.
Lives here rather than on any one surface because three need it: the tool
row, the DiffMessage that may replace it, and non_interactive's printed
output. A caveat produced in only one place is a caveat that goes missing on
whichever surface the user happens to be using — which is how -p came to
print an unqualified path for a change it could not verify.
Keyed on the outcome rather than on a FileOperationRecord so a widget
holding only the outcome produces the identical sentence, and the wording
cannot drift between surfaces. That holds for every outcome whose text is
self-contained; unreadable_after is the exception, degrading to a generic
reason without after_read_error. No current caller reaches it — that path
leaves record.diff unset, so no DiffMessage mounts — but a future one
passing only the outcome would render a weaker sentence than the tool row.
Return display_caveat for a successful completed record.
An untrusted pre-image describes what a successful operation could not
verify. It is not a property of a failed tool call: showing its success
wording after the tool error would contradict the actual result. status
cannot make that distinction because a successful write or edit whose
post-image cannot be read is also recorded as an error.
Compute a unified diff between before and after content.
Convert a virtual/relative path to a physical filesystem path.
Return whether a path points at a credential/secret file.
Best-effort, filename-based, case-insensitive heuristic. It matches .env
and its variants (e.g. .env.local), well-known credential filenames, and
private-key/keystore suffixes, and is used to suppress diff/content
rendering for those files so their contents are not shown in the terminal
UI or scrollback. It classifies by name only, not content, so
secret-bearing files with unrecognized names still render.
Format a path for display.
Collect summary info and diff for HITL approvals.
Line counts for a change, named so the pair cannot be swapped silently.
Keyword-only and frozen so that claim holds at construction as well as in transit: a positional pair is exactly the transposition this type exists to rule out, and the counts are read long after they are computed.
Data used to render HITL previews.
Line and byte level metrics for a file operation.
Track a single filesystem tool call.
Collect file operation metrics during an interaction.